package com.lemon.springcloud.core.component.zookeeper;

import com.lemon.springcloud.core.bean.ServiceInfo;
import com.lemon.springcloud.core.config.LemonSpringBootConfig;
import com.lemon.springcloud.core.constant.LemonBootConstant;
import com.lemon.springcloud.core.initial.LemonApplicationInitializer;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.discovery.EurekaClient;
import org.apache.commons.lang.StringUtils;
import org.apache.zookeeper.CreateMode;
import org.springframework.context.ApplicationContext;

import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Class Name: ZkRegister
 * Create Date: 18-3-22 下午7:02
 * Creator: lemon
 * Version: v1.0
 * Updater:
 * Date Time:
 * Description:
 */
public class ZkRegister {

    private static final String MOP_SPRING_CLOUD_ZK_CONFIG_ROOT = "/mop_cloud_service";
    private static final String SEPARATOR = "/";
    private static String MOP_SPRING_CLOUD_ZK_SYSTEM_PATH = null;
    private static String MOP_SPRING_CLOUD_ZK_ALIVE_PATH = null;
    private static String MOP_SPRING_CLOUD_ZK_META_PATH = null;

    public static String getMopSpringCloudZkMetaPath() {
        return MOP_SPRING_CLOUD_ZK_META_PATH;
    }

    private static String MOP_SPRING_CLOUD_ZK_UUID_PATH = null;

    private ApplicationContext context;
    private static ZkBoot client = null;

    public ZkRegister(ApplicationContext context) {
        this.context = context;
        if (LemonApplicationInitializer.getLemonBuildInZkEnable()) {
            client = context.getBean(ZkBoot.class);
            client.setZkAddress(LemonSpringBootConfig.getZookeeperGroupInfo().get("zookeeper.default"));
        }
    }

    /**
     * initialize current api base information, including session an global config info.
     */
    public void initialize() {
        if (!client.isInitialized()) {
            client.init();
        }
        if (client.connected()) {
            // Create Base Zookeeper Data Node
           LemonApplicationInitializer.setZkConnected(true);
            client.createIfNotExists(MOP_SPRING_CLOUD_ZK_CONFIG_ROOT);
            String corePath = MOP_SPRING_CLOUD_ZK_CONFIG_ROOT + SEPARATOR + "core";
            client.createIfNotExists(corePath);
            MOP_SPRING_CLOUD_ZK_SYSTEM_PATH = corePath + SEPARATOR + "system";
            MOP_SPRING_CLOUD_ZK_ALIVE_PATH = MOP_SPRING_CLOUD_ZK_SYSTEM_PATH + SEPARATOR + "alive";
            client.createIfNotExists(MOP_SPRING_CLOUD_ZK_SYSTEM_PATH);

            String uuid = ServiceInfo.getServiceUUID();
            MOP_SPRING_CLOUD_ZK_UUID_PATH = MOP_SPRING_CLOUD_ZK_ALIVE_PATH + SEPARATOR + uuid;
            MOP_SPRING_CLOUD_ZK_META_PATH = MOP_SPRING_CLOUD_ZK_UUID_PATH + ".META.properties";

            this.registerMeta();
        }
    }

    /**
     * register api meta data
     */
    private void registerMeta() {
        String alivePath = MOP_SPRING_CLOUD_ZK_ALIVE_PATH;
        client.createIfNotExists(alivePath);

        String uuidPath = MOP_SPRING_CLOUD_ZK_UUID_PATH;
        client.createIfNotExists(uuidPath, CreateMode.EPHEMERAL);

        String metaPath = MOP_SPRING_CLOUD_ZK_META_PATH;
        client.createIfNotExists(metaPath, CreateMode.EPHEMERAL);

        String metaData = LemonBootConstant.META_KEY_UUID + "=" + ServiceInfo.getServiceUUID() + "\n" +
                LemonBootConstant.META_KEY_ROUTE_TAG + "=" + ServiceInfo.getServiceRouteTag() + "\n" +
                LemonBootConstant.META_KEY_WEIGHT + "=" + ServiceInfo.getServiceWeight() + "\n";
        client.setData(metaPath, metaData.getBytes());

        this.watchSession();
    }

    /**
     * Watch and update meta session.
     */
    private void watchSession() {

        String metaPath = MOP_SPRING_CLOUD_ZK_META_PATH;
        client.getStrAndWatch(metaPath, new ZkBoot.ZkChangeCallback() {

            @Override
            public void strValChanged(String val) {
                EurekaClient eurekaClient = context.getBean(EurekaClient.class);
                ApplicationInfoManager applicationInfoManager = eurekaClient.getApplicationInfoManager();
                Properties properties = new Properties();
                try {
                    properties.load(new StringReader(val));

                    Map<String, String> metadata = new HashMap<>();
                    for (Object key : properties.keySet()) {
                        String keyStr = String.valueOf(key);
                        metadata.put(keyStr, properties.getProperty(keyStr));
                    }
                    if (applicationInfoManager != null) {
                        applicationInfoManager.registerAppMetadata(metadata);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        });
    }

    /**
     * Update Existed Meta Data
     *
     * @param key   meta key
     * @param value meta value
     * @return true if update success else false.
     */
    public static boolean updateMeta(String key, String value) {
        if (StringUtils.isEmpty(value)) {
            return false;
        }
        String metaStr = client.getStrAndWatch(MOP_SPRING_CLOUD_ZK_META_PATH);
        Properties properties = new Properties();
        try {
            properties.load(new StringReader(metaStr));
            for (Object k : properties.keySet()) {
                String kStr = String.valueOf(k);
                if (kStr.equals(key)) {
                    properties.setProperty(kStr, value);

                    StringBuilder builder = new StringBuilder();
                    for (Object k2 : properties.keySet()) {
                        String kStr2 = String.valueOf(k2);

                        builder.append(kStr2).append("=").append(properties.getProperty(kStr2)).append("\n");
                    }
                    return client.setData(MOP_SPRING_CLOUD_ZK_META_PATH, builder.toString().getBytes());
                }
            }
            return false;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * clear register api meta data and session node
     */
    public void clearSession() {
        String alivePath = MOP_SPRING_CLOUD_ZK_SYSTEM_PATH + SEPARATOR + "alive";
        String uuidPath = alivePath + SEPARATOR + ServiceInfo.getServiceUUID();
        String metaPath = uuidPath + ".META.properties";
        client.delete(uuidPath);
        client.delete(metaPath);
    }
}
